# set src file
set(NANOFAISS_SRC
        Clustering.cpp
        Index.cpp
        IndexFlat.cpp
        IndexFlatCodes.cpp
        IndexHNSW.cpp
        IndexIVF.cpp
        IndexIVFFlat.cpp
        IndexLightWeight.cpp
        IndexMoE.cpp
        impl/FaissException.cpp
        invlists/DirectMap.h
        invlists/InvertedLists.cpp
        utils/distances.cpp
        utils/distances_simd.cpp
        utils/Heap.cpp
        utils/random.cpp
        utils/utils.cpp
)

# set header file
set(NANOFAISS_HEADERS
        Clustering.h
        Index.h
        IndexFlat.h
        IndexFlatCodes.h
        IndexHNSW.h
        IndexIVF.h
        IndexIVFFlat.h
        IndexLightWeight.h
        IndexMoE.h
        MetricType.h
        impl/AuxIndexStructures.h
        impl/DistanceComputer.h
        impl/FaissAssert.h
        impl/FaissException.h
        impl/platform_macros.h
        impl/ResultHandler.h
        invlists/DirectMap.h
        invlists/InvertedLists.h
        utils/distances.h
        utils/Heap.h
        utils/ordered_key_value.h
        utils/random.h
        utils/utils.h
)

# Export FAISS_HEADERS variable to parent scope.
set(NANOFAISS_HEADERS ${NANOFAISS_HEADERS} PARENT_SCOPE)

# compile to nanofaiss lib, used for target link
add_library(nanofaiss ${NANOFAISS_SRC})

# as nanofaiss avx2 lib
add_library(nanofaiss_avx2 ${NANOFAISS_SRC})

# decide whether compile according to the FAISS_OPT_LEVEL
if(NOT NANOFAISS_OPT_LEVEL STREQUAL "avx2")
    set_target_properties(nanofaiss_avx2 PROPERTIES EXCLUDE_FROM_ALL TRUE)
endif()

# same with avx2
add_library(nanofaiss_avx512 ${NANOFAISS_SRC})
if(NOT NANOFAISS_OPT_LEVEL STREQUAL "avx512")
    set_target_properties(nanofaiss_avx512 PROPERTIES EXCLUDE_FROM_ALL TRUE)
endif()

if(NOT WIN32)
    # All modern CPUs support F, CD, VL, DQ, BW extensions.
    # Ref: https://en.wikipedia.org/wiki/AVX512
    target_compile_options(nanofaiss_avx512 PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-mavx2 -mfma -mf16c -mavx512f -mavx512cd -mavx512vl -mavx512dq -mavx512bw -mpopcnt>)
else()
    target_compile_options(nanofaiss_avx512 PRIVATE $<$<COMPILE_LANGUAGE:CXX>:/arch:AVX512>)
    # we need bigobj for the swig wrapper
    add_compile_options(/bigobj)
endif()

# tell cmake where to find the header file, and all need the lib file will include the directory
# Handle `#include <nanofaiss/foo.h>`.
target_include_directories(nanofaiss PUBLIC
        $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>)
# Handle `#include <nanofaiss/foo.h>`.
target_include_directories(nanofaiss_avx2 PUBLIC
        $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>)
# Handle `#include <nanofaiss/foo.h>`.
target_include_directories(nanofaiss_avx512 PUBLIC
        $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>)

# generate PIC for shared/dynamic lib
# sencond is windows rountine
set_target_properties(nanofaiss PROPERTIES
        POSITION_INDEPENDENT_CODE ON
        WINDOWS_EXPORT_ALL_SYMBOLS ON
)
set_target_properties(nanofaiss_avx2 PROPERTIES
        POSITION_INDEPENDENT_CODE ON
        WINDOWS_EXPORT_ALL_SYMBOLS ON
)
set_target_properties(nanofaiss_avx512 PROPERTIES
        POSITION_INDEPENDENT_CODE ON
        WINDOWS_EXPORT_ALL_SYMBOLS ON
)

# set an compile definition just for target
target_compile_definitions(nanofaiss PRIVATE FINTEGER=int)
target_compile_definitions(nanofaiss_avx2 PRIVATE FINTEGER=int)
target_compile_definitions(nanofaiss_avx512 PRIVATE FINTEGER=int)

# find the package that need to be linked to the nanofaiss
find_package(OpenMP REQUIRED)
target_link_libraries(nanofaiss PRIVATE OpenMP::OpenMP_CXX)
target_link_libraries(nanofaiss_avx2 PRIVATE OpenMP::OpenMP_CXX)
target_link_libraries(nanofaiss_avx512 PRIVATE OpenMP::OpenMP_CXX)

find_package(MKL)
if(MKL_FOUND)
    target_link_libraries(nanofaiss PRIVATE ${MKL_LIBRARIES})
    target_link_libraries(nanofaiss_avx2 PRIVATE ${MKL_LIBRARIES})
    target_link_libraries(nanofaiss_avx512 PRIVATE ${MKL_LIBRARIES})
else()
    find_package(BLAS REQUIRED)
    target_link_libraries(nanofaiss PRIVATE ${BLAS_LIBRARIES})
    target_link_libraries(nanofaiss_avx2 PRIVATE ${BLAS_LIBRARIES})
    target_link_libraries(nanofaiss_avx512 PRIVATE ${BLAS_LIBRARIES})

    find_package(LAPACK REQUIRED)
    target_link_libraries(nanofaiss PRIVATE ${LAPACK_LIBRARIES})
    target_link_libraries(nanofaiss_avx2 PRIVATE ${LAPACK_LIBRARIES})
    target_link_libraries(nanofaiss_avx512 PRIVATE ${LAPACK_LIBRARIES})
endif()

# install nanofaiss as nano-nanofaiss-target at GNU_DIR style
install(TARGETS nanofaiss
        EXPORT nanofaiss-targets
        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
        INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

if(NANOFAISS_OPT_LEVEL STREQUAL "avx2")
    install(TARGETS nanofaiss_avx2
            EXPORT nanofaiss-targets
            ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
            LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    )
endif()
if(NANOFAISS_OPT_LEVEL STREQUAL "avx512")
    install(TARGETS nanofaiss_avx2 faiss_avx512
            EXPORT nanofaiss-targets
            ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
            LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    )
endif()

# copy header file to ${CMAKE_INSTALL_INCLUDEDIR}/nanofaiss/${dir}
foreach(header ${NANOFAISS_HEADERS})
    get_filename_component(dir ${header} DIRECTORY )
    install(FILES ${header}
            DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/nanofaiss/${dir}
    )
endforeach()

# aim for export
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
        "${PROJECT_BINARY_DIR}/cmake/nanofaiss-config-version.cmake"
        VERSION ${CMAKE_PROJECT_VERSION}
        COMPATIBILITY AnyNewerVersion
)

configure_file(${PROJECT_SOURCE_DIR}/cmake/nanofaiss-config.cmake.in
        ${PROJECT_BINARY_DIR}/cmake/nanofaiss-config.cmake
        COPYONLY
)
install(FILES ${PROJECT_BINARY_DIR}/cmake/nanofaiss-config.cmake
        ${PROJECT_BINARY_DIR}/cmake/nanofaiss-config-version.cmake
        DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/nanofaiss
)

install(EXPORT nanofaiss-targets
        DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/nanofaiss
)